package com.wiscamp.ninechapters.problems.domain.models.problems.answers;

import com.wiscamp.ninechapters.problems.math.LatexFraction;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import java.util.regex.Pattern;

/**
 * 分数处理工具包
 */
public class FractionUtils {

    private FractionUtils() {
    }

    private static final String FRACTION_PREFIX = "\\frac";

    /**
     * 将文本转换成分数对象 （文本格式: -3 1/2）
     *
     * @param text
     * @return
     */
    public static LatexFraction toSlashFraction(String text) {
        boolean hasNumber = false;

        var patternText1 = "\\d+\\s+\\d+\\s*/\\s*\\d+";
        var pattern1 = Pattern.compile(patternText1);
        var matcher1 = pattern1.matcher(text);
        if (matcher1.find()) {
            hasNumber = true;
        }

        String patternText = "";
        if (!hasNumber) {
            boolean hasSymbol = text.contains("-") || text.contains("+");
            if (hasSymbol)
                patternText = "(\\-|\\+)?(\\s*\\d+\\s*)/(\\s*\\d+\\s*)";
            else
                patternText = "(\\s*\\d+\\s*)/(\\s*\\d+\\s*)";

            var pattern = Pattern.compile(patternText);
            var matcher = pattern.matcher(text);
            if (matcher.find()) {
                var fraction = new LatexFraction();
                var symbol = "";
                var numeratorString = "";
                var denominatorString = "";

                if (hasSymbol) {
                    symbol = matcher.group(1).trim();
                    numeratorString = matcher.group(2).trim();
                    denominatorString = matcher.group(3).trim();
                } else {
                    symbol = "+";
                    numeratorString = matcher.group(1).trim();
                    denominatorString = matcher.group(2).trim();
                }
                try {
                    var numerator = Integer.parseInt(numeratorString);
                    var denominator = Integer.parseInt(denominatorString);
                    fraction.setNumerator(numerator);
                    fraction.setDenominator(denominator);
                    fraction.setSymbol(symbol);
                    return fraction;
                } catch (NumberFormatException ex) {
                    return null;
                }
            } else return null;
        } else {
            boolean hasSymbol = text.contains("-") || text.contains("+");
            if (hasSymbol)
                patternText = "(\\-|\\+)?(\\s*\\d+\\s*)(\\s*\\d+\\s*)/(\\s*\\d+\\s*)";
            else
                patternText = "(\\s*\\d+\\s*)(\\s*\\d+\\s*)/(\\s*\\d+\\s*)";

            var pattern = Pattern.compile(patternText);
            var matcher = pattern.matcher(text);
            if (matcher.find()) {
                var fraction = new LatexFraction();
                var symbol = "";
                var numberString = "";
                var numeratorString = "";
                var denominatorString = "";

                if (hasSymbol) {
                    symbol = matcher.group(1).trim();
                    numberString = matcher.group(2).trim();
                    numeratorString = matcher.group(3).trim();
                    denominatorString = matcher.group(4).trim();
                } else {
                    symbol = "+";
                    numberString = matcher.group(1).trim();
                    numeratorString = matcher.group(2).trim();
                    denominatorString = matcher.group(3).trim();
                }
                try {
                    var number = Integer.parseInt(numberString);
                    var numerator = Integer.parseInt(numeratorString);
                    var denominator = Integer.parseInt(denominatorString);
                    fraction.setPrefixNumber(number);
                    fraction.setNumerator(numerator);
                    fraction.setDenominator(denominator);
                    fraction.setSymbol(symbol);
                    return fraction;
                } catch (NumberFormatException ex) {
                    return null;
                }
            } else return null;
        }
    }

    /**
     * 将Latex文本转换成分数对象 （Latex文本格式: -3 \qfrac{1}{2}）
     *
     * @param text
     * @return
     */
    public static LatexFraction toLatexFraction(String text) {
        var latex = text.trim()
                .replace("tfrac", "frac")
                .replace("dfrac", "frac");

        if (!latex.contains(FRACTION_PREFIX)) return null;

        latex = latex.replace(FRACTION_PREFIX, "")
                .replace("}{", "/")
                .replace("{", "").replace("}", "");

        return toSlashFraction(latex);
    }

    /**
     * 将Latex文本转换成分数对象
     * 支持格式： -3 1/2 或 -3 \qfrac{1}{2}
     *
     * @param text
     * @return
     */
    public static LatexFraction toFraction(String text) {
        if (text.contains(FRACTION_PREFIX))
            text = text.trim().replace("\\tfrac", FRACTION_PREFIX);
        if (text.contains("\\qfrac"))
            text = text.trim().replace("\\qfrac", FRACTION_PREFIX);
        if (text.contains("\\dfrac"))
            text = text.trim().replace("\\dfrac", FRACTION_PREFIX);

        if (text.contains(FRACTION_PREFIX)) return toLatexFraction(text);
        if (text.contains("/")) return toSlashFraction(text);
        return null;
    }

    /**
     * 将分数对象换算成小数
     *
     * @param latexFractionalNumber
     * @return
     */
    public static double fractionToDouble(@NotBlank LatexFraction latexFractionalNumber) {
        var f1 = toProperFraction(latexFractionalNumber);
        if (f1.getDenominator() == 0) return Double.MIN_VALUE;
        double numerator = f1.getNumerator() * 1.0;
        double denominator = f1.getDenominator() * 1.0;
        double result;
        result = f1.getPrefixNumber() + (numerator / denominator);
        if (f1.getSymbol().equals("-"))
            result = -1.0 * result;
        return result;
    }

    /**
     * 比较两个分数对象是否相同
     *
     * @param source
     * @param target
     * @return
     */
    public static boolean compareFraction(@NotNull LatexFraction source, @NotNull LatexFraction target) {
        var f1 = toProperFraction(source);
        var f2 = toProperFraction(target);
        return f1.getSymbol().contains(f2.getSymbol())
                && f1.getPrefixNumber() == f2.getPrefixNumber()
                && f1.getNumerator() == f2.getNumerator()
                && f1.getDenominator() == f2.getDenominator();
    }

    /**
     * 判断分数对象是否为真分数
     *
     * @param latexFractionalNumber
     * @return
     */
    public static boolean isProperFraction(@NotNull LatexFraction latexFractionalNumber) {
        return latexFractionalNumber.getNumerator() < latexFractionalNumber.getDenominator();
    }

    /**
     * 将分数对象转成真分数格式
     *
     * @param latexFractionalNumber
     * @return
     */
    public static LatexFraction toProperFraction(@NotNull LatexFraction latexFractionalNumber) {
        if (isProperFraction(latexFractionalNumber)) return latexFractionalNumber;

        var properFraction = new LatexFraction();
        int number = latexFractionalNumber.getPrefixNumber();
        int numerator = latexFractionalNumber.getNumerator();
        int denominator = latexFractionalNumber.getDenominator();

        if (numerator == denominator) {
            number = number + 1;
            numerator = 0;
            denominator = 0;
        } else {
            int x = numerator / denominator;
            int y = numerator - (x * denominator);
            number = number + x;
            numerator = y;
        }
        properFraction.setSymbol(latexFractionalNumber.getSymbol());
        properFraction.setPrefixNumber(number);
        properFraction.setNumerator(numerator);
        properFraction.setDenominator(denominator);
        return properFraction;
    }

}
